Legend

vector, lists, tibbles and functions

names of variables within tibbles

highlighted content

Topics Covered

  1. Visualizing data using ggplot2
  2. Data Joins

Visualizing data using ggplot2

ggplot2 is a declarative plotting library. The gg in ggplot2 stands for “Grammar of Graphics” a book on the principles of data visualization that the package is based on. According to this article:

grammar of graphics is a framework which follows a layered approach to describe and construct visualizations or graphics in a structured manner. A visualization involving multi-dimensional data often has multiple components or aspects, and leveraging this layered grammar of graphics helps us describe and understand each component involved in visualization — in terms of data, aesthetics, scale, objects and so on.

You can think about the layering of graphics in the same way as the principle of composition that you learnt last week i.e. building complex functionality by breaking things down into smaller pieces. In the context of data manipulation using dplyr we used %>%, here we use graphical layers.

Source: A Comprehensive Guide to the Grammar of Graphics for Effective Visualization of Multi-dimensional Data - Dipanjan Sarkar

There are seven layers in ggplot. All plots need a minimum of three out of these seven layers (the rest adopt default values if not specified). These are as follows:

  1. A data layer
  2. An aesthetics layer
  3. A geom or statistics layer

Lets start plotting!

Basic charts

In this section we will explore a few basic chart types. Lets also open the reference website for ggplot in a new tab so that we have it handy. We will be using the gapminder dataset to illustrate ggplot commands. This dataset is available as a package of the same name 1. Go ahead and install this on your computer. The gapminder dataset is displayed below. It provides data on life expectancy (lifeExp), population (pop) and GDP per capita (gdpPercap) for each country for five year intervals from 1952 to 2007.

rm(list = ls())
##load the packages
library(tidyverse); library(nycflights13); library(gapminder)
gapminder

Scatterplot

The first plot we will explore is the scatter-plot. A scatter-plot simply places a point on a cartesian (2-D) coordinate system for corresponding values of the x and y variables. Lets explore the relationship between year and life expectancy using a scatter-plot.

Go through the code below to identify the data, aesthetics and geom layers. As can be seen below, this is not a particularly useful chart. Each year in the data has several observations for lifeExp (one for each country), this results in a chart for which it is difficult to perceive a clear trend. In the next step we will see how to improve this chart.

##specify the data and aesthetics layer
ggplot(data = gapminder, aes(x = year, y = lifeExp)) +
    ##specifying the geom type
    geom_point() +
    ##adding x and y axis titles
    labs(x = "Year", y = "Life Expectancy")

Ninja Tasks

  1. Use a scatter-plot to explore the relationship between gdpPercap and lifeExp

🏆Solution🏆: The gapminder data consists of observations for each country for each year. We are interested in capturing the relationship between gdpPercap and the lifeExp. However, since the data is a time series, we need to average the gdpPercap and lifeExp for each country for the entire time series before plotting to smooth out time trends and avoid over-plotting 2. This would allow us to observe the relationship between the two variables more clearly without any confusing time trends 3.

gapminder %>% 
    ##find the mean gdp per capita and life expectancy for each country
    group_by(country) %>% 
    summarise(meanGDPperCap = mean(gdpPercap, na.rm = T), 
              meanLifeExp = mean(lifeExp, na.rm = T)) %>% 
    ##specify the data and aesthetics layer
    ggplot(., aes(x = meanGDPperCap, y = meanLifeExp)) + 
    ##specify the geom
    geom_point(colour = "blue", size = 2.5, alpha = 0.8) +
    ##specify titles
    labs(x = "Mean GDP per capita", 
         y = "Mean Life Expectancy", 
         title = "Life Expectancy increases with GDP per capita")

The scatter-plot shows one single point for each country in the dataset representing the mean lifeExp and gdpPercap. Make sure to read the code used to generate the chart carefully so that you understand it fully. Why are the colour and size paramters in the geom_point() not inside an aes()?

  1. Bonus question: Can you map the pop to a particular aesthetic so that it can be displayed on the chart too?

🏆Solution🏆: Lets map the size of each point to the average population. I also introduce an additional aesthetic mapping of colour to the continent variable. In addition, since there are large values in the gdpPercap, we can plot the log of the mean gdpPercap to smooth out the large numbers (watch the video below if you don’t fully understand logarithmic scales).

We can do this in two ways, first we could mutate meanGDPperCap by taking its log and plotting that (try this yourself)4, and the second option would be to use ggplots inbuilt scale_x_log10() command to transform the plotting scale instead of the variable. While the charts would largely look the same, the second option preserves the actual variable and plots it on the new scale. Plot the first option on your own to see if you can spot the difference between the plot below and that one.

The application of the log scale has the effect of making it appear as if the relationship between gdpPercap and lifeExp is linear. This is not true, since as we observed in the previous chart the true relationship between these two variables is parabolic i.e. as gdpPercap increases, the corresponding increase in lifeExp gets lower over time (probably because we can’t extend human lifespans beyond a certain limit, no matter how hard we try). This is one reason, why we should be extremely careful with scale transformations. We should only use them if we have a clear reason for why they are necessary. And when we do choose to use them we should have a clear sense for what they are doing to the interpretation of the chart.

gapminder %>% 
    ##Calculate country level information
    group_by(country) %>% 
    summarise(meanGDPperCap = mean(gdpPercap, na.rm = T), 
              meanLifeExp = mean(lifeExp, na.rm = T), 
              meanPop = mean(pop, na.rm = T),
              continent = unique(continent)) %>% 
    ##specify data and aesthetics
    ggplot(., aes(x = meanGDPperCap, 
                  y = meanLifeExp, 
                  size = meanPop, 
                  colour = continent)) + 
    ##specify the geom
    geom_point(alpha = 0.6) +
    ##apply scale transformation
    scale_x_log10() +
    ##specify the titles
    labs(x = "Mean GDP per capita", 
         y = "Mean Life Expectancy", 
         title = "Life Expectancy increases exth GDP per capita", 
         size = "Mean population",
         colour = "Continent") +
    ##adjust the position and layout of the legends
    theme(legend.position = "top",
          legend.box = "vertical",
          legend.spacing.y = unit(-8, "pt"),
          )

Line chart

Now lets use dplyr to calculate the average global lifeExp by year and plot that using a line. As can be seen below, the average global lifeExp has been increasing over the years.

gapminder %>% 
    ##group by year to calculate the yearly average life expectancy
    group_by(year) %>% 
    summarise(meanLifeExp = mean(lifeExp, na.rm = T)) %>% 
    ##draw a line marking the trend of life expectancy over years
    ggplot(., aes(x = year, y = meanLifeExp)) +
    geom_line() +
    labs(title = "Average life expectancy has been increasing", x = "Years", y = "Mean Life Expectancy")

Now lets go a bit further and explore the trend for lifeExp for different countries. No

ggplot(data = gapminder, mapping = aes(x = year, y = lifeExp)) +
    ##colour and group are mapped to continent and country and aesthetic is set to 0.5
    geom_line(mapping = aes(colour = continent, group = country), alpha = 0.5) +
    labs(title = "Countries in Africa have lower life expectancy", x = "Year", y = "Life Expectancy") +
    theme(
        legend.position = "top"
        
    )

In the chart above there are a few countries that experience sudden drops in lifeExp. These are because of genocides that occurred in Rwanda, Cambodia and China. The chart below used color and alpha mapping to highlight these values. Notice how, I use a alphaMapping and colorMapping variables to fix the alpha and colour aesthetics in the chart.[^6]

##repeat the same but with one line for each continent/country, (guess why there is a sudden drop for a few countries)?
alphaMapping <- if_else(gapminder$country %in% c("Rwanda", "Cambodia", "China"), 0.8, 0.1)
colorMapping <- if_else(gapminder$country %in% c("Rwanda", "Cambodia", "China"), "darkred", "black")
ggplot(data = gapminder, mapping = aes(x = year, y = lifeExp, group = country)) +
    geom_line(alpha = alphaMapping, colour = colorMapping) +
    labs(title = "The impact of genocides on life expectancy", x = "Year", y = "Life Expectancy")

Ninja Tasks

  1. Draw a line chart showing the trend for average gdpPercap through time for each continent

🏆Solution🏆: The chart below shows that poorer countries in the Africa and Asia are still lagging behind those in the West. According to Wikipedia convergence can be described as follows:

The idea of convergence in economics (also sometimes known as the catch-up effect) is the hypothesis that poorer economies’ per capita incomes will tend to grow at faster rates than richer economies. As a result, all economies should eventually converge in terms of per capita income.

gapminder %>% 
    group_by(continent, year) %>% 
    summarise(meanGDP = mean(gdpPercap, na.rm = T)) %>% 
    ggplot(., aes(x = year, y = meanGDP)) +
    geom_line(aes(colour = continent)) +
    labs(x = "Year", y = "Mean GDP per Capita", title = "Convergence where art thou?", colour = "Continent") +
    theme(legend.position = "top")

  1. Histogram Watch the video below to refresh your understanding of histograms. The plot below shows the histogram of gdpPerCap in the gapminder dataset. There is however a problem with this chart. It shows the distribution of gdpPercap over time. We are however not interested in the spread of gdpPercap across time, rather our interest is in seeing the spread across countries. In this case, we might be better off by filtering down to a single year and observing the distribution of gdpPercap across countries. Lets try this out with the training exercise.
ggplot(data = gapminder, aes(gdpPercap)) +
    geom_histogram(bins = 30)

Ninja Tasks

  1. Draw a histogram to show the characteristics of population

🏆Solution🏆: For this chart we select the latest year in the dataset and plot the distribution of the population. Could you think of the pros and cons for using this method versus the one in which we calculate the average population for each country over the entire dataset and plotting that?

gapminder %>% 
    filter(year == max(year, na.rm = T)) %>% 
    ggplot(., aes(pop)) +
    geom_histogram(bins = 60) +
    labs(x = "Population", y = "Count")

Bar chart

The chart below shows the average lifeExp for different continents for the most recent year in the data. Can you think of the reason why it might be slightly better to have only considered the most recent year when calculating the average lifeExp for a continent? Also notice, how the bars are aligned from the smallest to the tallest. Can you find out how I might have achieved this?

##draw a bar chart with average life expectancy in different continents
gapminder %>% 
    filter(year == max(year, na.rm = T)) %>% 
    group_by(continent) %>% 
    summarise(meanLifeExp = mean(lifeExp, na.rm = T)) %>% 
    ggplot(., aes(x = reorder(continent, meanLifeExp), y = meanLifeExp)) +
    geom_col() +
    labs(y = "Mean life expectancy") +
    theme(
        axis.title.x = element_blank()
    )

NA

Ninja Tasks

  1. Draw a bar chart showing the average lifeExp across years

🏆Solution🏆

gapminder %>% 
    group_by(year) %>% 
    summarise(meanLifeExp = mean(lifeExp, na.rm = T)) %>% 
    ggplot(data = ., aes(x = year, y = meanLifeExp)) +
    geom_col()

NA
gapminder %>% 
    group_by(year) %>% 
    summarise(meanGDP = mean(gdpPercap, na.rm = T)) %>% 
    ggplot(., aes(x = year, y = meanGDP)) +
    geom_col()

Facetting

##reuse some of the plots above as a facetted plot
ggplot(gapminder, aes(x = year, y = lifeExp)) +
    geom_jitter(width = 1.5, alpha = 0.6) +
    facet_wrap(~continent)

Ninja Tasks

  1. Show the relationship between gdpPercap and lifeExp using a faceted chart of your choice

Layering multiple geoms

##draw a line of fit in a chart of population and life expectancy
gapminder %>% 
    group_by(country) %>% 
    summarise(continent = unique(continent), meanPop = mean(pop, na.rm = T), meanLifeExp = mean(lifeExp, na.rm = T)) %>% 
    filter(meanPop < 5e7) %>% 
ggplot(., aes(x = meanPop, y = meanLifeExp)) +
    geom_jitter() + 
    geom_smooth(method = "lm", se = F)

Ninja Tasks

  1. Add a line of best fit to a scatter plot showing the relationship between gdpPercap and lifeExp

Adding summary stats to plots

##explore gdp per capita over the years using a jitter plot and stat_summary (rule of thumb, always stay as close to the data as possible)
    
ggplot(gapminder, aes(x = year, y = gdpPercap)) +
    geom_jitter()

##Using vline and hline

Ninja Tasks

  1. Add a vertical line showing the mean of population to the histogram showing its distribution

Add some style

##Use the plot from the previous section and add some pizazz (explore ggthemes, legend position etc)

Add some more complexity (based on time)

Coords

Scales

Data Joins

A data join combines two or more different data tables into one. Tables that can be connected to each other can be thought of as being “related”. A group of two or more related data tables would constitute a relational database. The variables that connect two or more tables with each other are called keys. For instance, consider two tables, one with information on GDP of countries and another with the population of countries. These two tables are related to one another through the shared key of country. If we were to manually calculate gdpPercap of a specific country we would look for the corresponding values for the country in each table to extract the respective values of GDP and population to calculate gdpPercap (GDP/population).

We will be using the practice datasets band_members and band_instruments provided in the dplyr package to understand the various concepts in data joins. I have added an additional row to the band_instruments table to make it easier to illustrate a few concepts. Please take a moment to look at the two data tables.

##display band_members
band_members
##remove band_members from the local environment
rm(band_instruments)
##add a row to band_instruments (give John an extra instrument)
band_instruments <- bind_rows(band_instruments, tibble(name = "John", plays = "keyboards"))
##display band_instruments
band_instruments

Keys can be of two types - primary and foreign. A key that uniquely identifies each row in a particular dataset is its primary key. For instance in the tibble band_members the primary key is the variable name since each row can be uniquely identified if we know the value of name. We can check if a variable is a primary key by grouping the data and checking the number of rows in each group. If there are more than one row in a group it means that the particularly grouping variable does not uniquely identify each row in the data i.e. there are multiple rows that have the same value for that grouping variable, hence it is not a primary key.

A primary key can also be a combination of two or more variables. For instance, in the case of band_instruments, name alone does not uniquely identify each row in the data (since we added an additional row for John). However, we can combine the variables name and play to uniquely identify every row in the data

There are 6 different types of joins. Four of these are called mutating joins and two are called filtering. A mutating join between two tables “x” and “y” creates a new table with columns from both x and y. A filtering join between x and y creates a new table with a subset of the rows from x and y. A mutating join can also lead to a subsetting of rows. Now lets look at each join in detail

The output below shows an inner join. Here we have used the common key variable “name” to perform an inner join of the band_members and band_instruments. The inner join and all the columns from x and yonly keeps the rows that have common keys between x and y. The graphic below illustrates an inner_join()

Source: R for Data Science by Garrett Grolemund and Hadley Wickham

left_join() is the most commonly used join. It preserves all the rows in x and

left_join(band_members, band_instruments)
Joining, by = "name"
right_join(band_members, band_instruments)
Joining, by = "name"
full_join(band_members, band_instruments)
Joining, by = "name"
semi_join(band_members, band_instruments)
Joining, by = "name"
anti_join(band_members, band_instruments)
Joining, by = "name"

Left Join

This is the most popular type of join (analogous to Vlookup in Excel).

library(nycflights13)
flightsWithWeather <- left_join(flights, weather, by = c("origin", "month", "day", "hour"))
weather

  1. The gapminder package is an excerpt of the data that exists here. This data was created by the Gapminder Foundation led by Hans Rosling.

  2. Over-plotting happens when data points are plotted on top of each other multiple times. It makes charts more confusing and difficult to read. It should be avoided as far as possible

  3. We could also do this by filtering out the most recent year to study the relationship. Both are valid options, however the former has the benefit of capturing more information since it takes into account the entire time series of data that is available to calculate the mean values, while the latter disregards all but one year of the data. In this case this suits our purposes of wanting to study the relationship between gdpPercap and lifeExp.

  4. You can do this using the following command ggplot(., aes(x = log(meanGDPperCap, base = 10), y = meanLifeExp)) instead of ggplot(., aes(x = meanGDPperCap, y = meanLifeExp)). Can you tell the difference between this plot and the one that was made using the scale transformation?

LS0tCnRpdGxlOiAiTnVtYmVycyBOaW5qYSBXZWVrIDI6IERhdGEgdmlzdWFsaXphdGlvbnMgYW5kIGpvaW5zIgphdXRob3I6IHwKICB8IEhhcmkgU3ViaGFzaAogIHwgRGF0YSBTY2llbnRpc3QgQE5SR0kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGhpZ2hsaWdodDoga2F0ZQogICAgc21hcnQ6IHllcwogICAgdGhlbWU6IGNvc21vCiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgaW5jbHVkZXM6IGFzc2V0cy9oZWFkZXIuaHRtbApjc3M6IGFzc2V0cy9jdXN0b20uY3NzCi0tLQo8ZGl2IHN0eWxlPSAiZmxvYXQ6cmlnaHQ7IHBvc2l0aW9uOiByZWxhdGl2ZTsgdG9wOiAtODBweDsgcGFkZGluZy1sZWZ0OiAwcHgiPgpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgb3V0LndpZHRoPScxMCUnfQojI2FkZCB0aGUgaWNvbgprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiYXNzZXRzL2ltYWdlcy9uaW5qYS1sb2dvMi5qcGciKQpgYGAKPC9kaXY+CgojI0xlZ2VuZApgdmVjdG9yLCBsaXN0cywgdGliYmxlcyBhbmQgZnVuY3Rpb25zYAoKPHNwYW4gY2xhc3M9InZhcmlhYmxlIj5uYW1lcyBvZiB2YXJpYWJsZXMgd2l0aGluIHRpYmJsZXMgPC9zcGFuPgoKPHNwYW4gY2xhc3M9ImhpZ2hsaWdodCI+aGlnaGxpZ2h0ZWQgY29udGVudDwvc3Bhbj4KCiMjVG9waWNzIENvdmVyZWQKCjEuIFZpc3VhbGl6aW5nIGRhdGEgdXNpbmcgZ2dwbG90MgoyLiBEYXRhIEpvaW5zCgo8ZGl2IGNsYXNzPSJmdWxsLXdpZHRoIj4KCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiYXNzZXRzL2ltYWdlcy9pYW4tZG9vbGV5LTQwNzg0Ni11bnNwbGFzaC5qcGciKQpgYGAKCjwvZGl2PgoKIyNWaXN1YWxpemluZyBkYXRhIHVzaW5nIGdncGxvdDIKZ2dwbG90MiBpcyBhIGRlY2xhcmF0aXZlIHBsb3R0aW5nIGxpYnJhcnkuIFRoZSBnZyBpbiBnZ3Bsb3QyIHN0YW5kcyBmb3IgIkdyYW1tYXIgb2YgR3JhcGhpY3MiIGEgYm9vayBvbiB0aGUgcHJpbmNpcGxlcyBvZiBkYXRhIHZpc3VhbGl6YXRpb24gdGhhdCB0aGUgcGFja2FnZSBpcyBiYXNlZCBvbi4gQWNjb3JkaW5nIHRvIHRoaXMgW2FydGljbGVdKGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS9hLWNvbXByZWhlbnNpdmUtZ3VpZGUtdG8tdGhlLWdyYW1tYXItb2YtZ3JhcGhpY3MtZm9yLWVmZmVjdGl2ZS12aXN1YWxpemF0aW9uLW9mLW11bHRpLWRpbWVuc2lvbmFsLTFmOTJiNGVkNDE0OSk6Cgo+Z3JhbW1hciBvZiBncmFwaGljcyBpcyBhIGZyYW1ld29yayB3aGljaCBmb2xsb3dzIGEgbGF5ZXJlZCBhcHByb2FjaCB0byBkZXNjcmliZSBhbmQgY29uc3RydWN0IHZpc3VhbGl6YXRpb25zIG9yIGdyYXBoaWNzIGluIGEgc3RydWN0dXJlZCBtYW5uZXIuIEEgdmlzdWFsaXphdGlvbiBpbnZvbHZpbmcgbXVsdGktZGltZW5zaW9uYWwgZGF0YSBvZnRlbiBoYXMgbXVsdGlwbGUgY29tcG9uZW50cyBvciBhc3BlY3RzLCBhbmQgbGV2ZXJhZ2luZyB0aGlzIGxheWVyZWQgZ3JhbW1hciBvZiBncmFwaGljcyBoZWxwcyB1cyBkZXNjcmliZSBhbmQgdW5kZXJzdGFuZCBlYWNoIGNvbXBvbmVudCBpbnZvbHZlZCBpbiB2aXN1YWxpemF0aW9u4oCK4oCU4oCKaW4gdGVybXMgb2YgZGF0YSwgYWVzdGhldGljcywgc2NhbGUsIG9iamVjdHMgYW5kIHNvIG9uLgoKWW91IGNhbiB0aGluayBhYm91dCB0aGUgbGF5ZXJpbmcgb2YgZ3JhcGhpY3MgaW4gdGhlIHNhbWUgd2F5IGFzIHRoZSBwcmluY2lwbGUgb2YgY29tcG9zaXRpb24gdGhhdCB5b3UgbGVhcm50IGxhc3Qgd2VlayBpLmUuIGJ1aWxkaW5nIGNvbXBsZXggZnVuY3Rpb25hbGl0eSBieSBicmVha2luZyB0aGluZ3MgZG93biBpbnRvIHNtYWxsZXIgcGllY2VzLiBJbiB0aGUgY29udGV4dCBvZiBkYXRhIG1hbmlwdWxhdGlvbiB1c2luZyBgZHBseXJgIHdlIHVzZWQgYCAlPiVgLCBoZXJlIHdlIHVzZSBncmFwaGljYWwgbGF5ZXJzLgoKPGRpdiBjbGFzcz0iY2VudGVyLWNvbnRhaW5lciI+CmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiYXNzZXRzL2ltYWdlcy9sYXllcnMgb2YgZ2dwbG90LnBuZyIpCmBgYAo8L2Rpdj4KPHNtYWxsPioqU291cmNlKio6IEEgQ29tcHJlaGVuc2l2ZSBHdWlkZSB0byB0aGUgR3JhbW1hciBvZiBHcmFwaGljcyBmb3IgRWZmZWN0aXZlIFZpc3VhbGl6YXRpb24gb2YgTXVsdGktZGltZW5zaW9uYWwgRGF0YSAtIERpcGFuamFuIFNhcmthcjwvc21hbGw+CgpUaGVyZSBhcmUgc2V2ZW4gbGF5ZXJzIGluIGdncGxvdC4gQWxsIHBsb3RzIG5lZWQgYSBtaW5pbXVtIG9mIHRocmVlIG91dCBvZiB0aGVzZSBzZXZlbiBsYXllcnMgKHRoZSByZXN0IGFkb3B0IGRlZmF1bHQgdmFsdWVzIGlmIG5vdCBzcGVjaWZpZWQpLiBUaGVzZSBhcmUgYXMgZm9sbG93czoKCjEuIEEgZGF0YSBsYXllcgoyLiBBbiBhZXN0aGV0aWNzIGxheWVyCjMuIEEgZ2VvbSBvciBzdGF0aXN0aWNzIGxheWVyCgpMZXRzIHN0YXJ0IHBsb3R0aW5nIQoKIyMjQmFzaWMgY2hhcnRzCkluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIGV4cGxvcmUgYSBmZXcgYmFzaWMgY2hhcnQgdHlwZXMuIExldHMgYWxzbyBvcGVuIHRoZSBbcmVmZXJlbmNlIHdlYnNpdGUgZm9yIGdncGxvdF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2luZGV4Lmh0bWwpIGluIGEgbmV3IHRhYiBzbyB0aGF0IHdlIGhhdmUgaXQgaGFuZHkuIFdlIHdpbGwgYmUgdXNpbmcgdGhlIGdhcG1pbmRlciBkYXRhc2V0IHRvIGlsbHVzdHJhdGUgZ2dwbG90IGNvbW1hbmRzLiBUaGlzIGRhdGFzZXQgaXMgYXZhaWxhYmxlIGFzIGEgcGFja2FnZSBvZiB0aGUgc2FtZSBuYW1lIFteMV0uIEdvIGFoZWFkIGFuZCBpbnN0YWxsIHRoaXMgb24geW91ciBjb21wdXRlci4gVGhlIGdhcG1pbmRlciBkYXRhc2V0IGlzIGRpc3BsYXllZCBiZWxvdy4gSXQgcHJvdmlkZXMgZGF0YSBvbiBsaWZlIGV4cGVjdGFuY3kgKDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+bGlmZUV4cDwvc3Bhbj4pLCBwb3B1bGF0aW9uICg8c3BhbiBjbGFzcz0idmFyaWFibGUiPnBvcDwvc3Bhbj4pIGFuZCBHRFAgcGVyIGNhcGl0YSAoPHNwYW4gY2xhc3M9InZhcmlhYmxlIj5nZHBQZXJjYXA8L3NwYW4+KSBmb3IgZWFjaCBjb3VudHJ5IGZvciBmaXZlIHllYXIgaW50ZXJ2YWxzIGZyb20gYHIgbWluKGdhcG1pbmRlciR5ZWFyKWAgdG8gYHIgbWF4KGdhcG1pbmRlciR5ZWFyKWAuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kcm0obGlzdCA9IGxzKCkpCiMjbG9hZCB0aGUgcGFja2FnZXMKbGlicmFyeSh0aWR5dmVyc2UpOyBsaWJyYXJ5KG55Y2ZsaWdodHMxMyk7IGxpYnJhcnkoZ2FwbWluZGVyKQpnYXBtaW5kZXIKYGBgCgojIyNTY2F0dGVycGxvdApUaGUgZmlyc3QgcGxvdCB3ZSB3aWxsIGV4cGxvcmUgaXMgdGhlIHNjYXR0ZXItcGxvdC4gQSBzY2F0dGVyLXBsb3Qgc2ltcGx5IHBsYWNlcyBhIHBvaW50IG9uIGEgY2FydGVzaWFuICgyLUQpIGNvb3JkaW5hdGUgc3lzdGVtIGZvciBjb3JyZXNwb25kaW5nIHZhbHVlcyBvZiB0aGUgeCBhbmQgeSB2YXJpYWJsZXMuIExldHMgZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4geWVhciBhbmQgbGlmZSBleHBlY3RhbmN5IHVzaW5nIGEgc2NhdHRlci1wbG90LiAKCkdvIHRocm91Z2ggdGhlIGNvZGUgYmVsb3cgdG8gaWRlbnRpZnkgdGhlIGRhdGEsIGFlc3RoZXRpY3MgYW5kIGdlb20gbGF5ZXJzLiBBcyBjYW4gYmUgc2VlbiBiZWxvdywgdGhpcyBpcyBub3QgYSBwYXJ0aWN1bGFybHkgdXNlZnVsIGNoYXJ0LiBFYWNoIHllYXIgaW4gdGhlIGRhdGEgaGFzIHNldmVyYWwgb2JzZXJ2YXRpb25zIGZvciA8c3BhbiBjbGFzcz0idmFyaWFibGUiPmxpZmVFeHA8L3NwYW4+IChvbmUgZm9yIGVhY2ggY291bnRyeSksIHRoaXMgcmVzdWx0cyBpbiBhIGNoYXJ0IGZvciB3aGljaCBpdCBpcyBkaWZmaWN1bHQgdG8gcGVyY2VpdmUgYSBjbGVhciB0cmVuZC4gSW4gdGhlIG5leHQgc3RlcCB3ZSB3aWxsIHNlZSBob3cgdG8gaW1wcm92ZSB0aGlzIGNoYXJ0LgoKYGBge3J9CiMjc3BlY2lmeSB0aGUgZGF0YSBhbmQgYWVzdGhldGljcyBsYXllcgpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlciwgYWVzKHggPSB5ZWFyLCB5ID0gbGlmZUV4cCkpICsKICAgICMjc3BlY2lmeWluZyB0aGUgZ2VvbSB0eXBlCiAgICBnZW9tX3BvaW50KCkgKwogICAgIyNhZGRpbmcgeCBhbmQgeSBheGlzIHRpdGxlcwogICAgbGFicyh4ID0gIlllYXIiLCB5ID0gIkxpZmUgRXhwZWN0YW5jeSIpCmBgYAoK4pqhKioqTmluamEgVGFza3MqKirimqEKCjEuIFVzZSBhIHNjYXR0ZXItcGxvdCB0byBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiA8c3BhbiBjbGFzcz0idmFyaWFibGUiPmdkcFBlcmNhcDwvc3Bhbj4gYW5kIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+bGlmZUV4cDwvc3Bhbj4KCjxkaXYgY2xhc3M9InNvbHV0aW9uIj4K8J+PhioqKlNvbHV0aW9uKioq8J+PhjogVGhlIGdhcG1pbmRlciBkYXRhIGNvbnNpc3RzIG9mIG9ic2VydmF0aW9ucyBmb3IgZWFjaCBjb3VudHJ5IGZvciBlYWNoIHllYXIuIFdlIGFyZSBpbnRlcmVzdGVkIGluIGNhcHR1cmluZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gPHNwYW4gY2xhc3M9InZhcmlhYmxlIj5nZHBQZXJjYXA8L3NwYW4+IGFuZCB0aGUgPHNwYW4gY2xhc3M9InZhcmlhYmxlIj5saWZlRXhwPC9zcGFuPi4gSG93ZXZlciwgc2luY2UgdGhlIGRhdGEgaXMgYSB0aW1lIHNlcmllcywgd2UgbmVlZCB0byBhdmVyYWdlIHRoZSA8c3BhbiBjbGFzcz0idmFyaWFibGUiPmdkcFBlcmNhcDwvc3Bhbj4gYW5kIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+bGlmZUV4cDwvc3Bhbj4gZm9yIGVhY2ggY291bnRyeSBmb3IgdGhlIGVudGlyZSB0aW1lIHNlcmllcyBiZWZvcmUgcGxvdHRpbmcgdG8gc21vb3RoIG91dCB0aW1lIHRyZW5kcyBhbmQgYXZvaWQgb3Zlci1wbG90dGluZyBbXjJdLiBUaGlzIHdvdWxkIGFsbG93IHVzIHRvIG9ic2VydmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzIG1vcmUgY2xlYXJseSB3aXRob3V0IGFueSBjb25mdXNpbmcgdGltZSB0cmVuZHMgW14zXS4KCmBgYHtyfQpnYXBtaW5kZXIgJT4lIAogICAgIyNmaW5kIHRoZSBtZWFuIGdkcCBwZXIgY2FwaXRhIGFuZCBsaWZlIGV4cGVjdGFuY3kgZm9yIGVhY2ggY291bnRyeQogICAgZ3JvdXBfYnkoY291bnRyeSkgJT4lIAogICAgc3VtbWFyaXNlKG1lYW5HRFBwZXJDYXAgPSBtZWFuKGdkcFBlcmNhcCwgbmEucm0gPSBUKSwgCiAgICAgICAgICAgICAgbWVhbkxpZmVFeHAgPSBtZWFuKGxpZmVFeHAsIG5hLnJtID0gVCkpICU+JSAKICAgICMjc3BlY2lmeSB0aGUgZGF0YSBhbmQgYWVzdGhldGljcyBsYXllcgogICAgZ2dwbG90KC4sIGFlcyh4ID0gbWVhbkdEUHBlckNhcCwgeSA9IG1lYW5MaWZlRXhwKSkgKyAKICAgICMjc3BlY2lmeSB0aGUgZ2VvbQogICAgZ2VvbV9wb2ludChjb2xvdXIgPSAiYmx1ZSIsIHNpemUgPSAyLjUsIGFscGhhID0gMC44KSArCiAgICAjI3NwZWNpZnkgdGl0bGVzCiAgICBsYWJzKHggPSAiTWVhbiBHRFAgcGVyIGNhcGl0YSIsIAogICAgICAgICB5ID0gIk1lYW4gTGlmZSBFeHBlY3RhbmN5IiwgCiAgICAgICAgIHRpdGxlID0gIkxpZmUgRXhwZWN0YW5jeSBpbmNyZWFzZXMgd2l0aCBHRFAgcGVyIGNhcGl0YSIpCmBgYAoKVGhlIHNjYXR0ZXItcGxvdCBzaG93cyBvbmUgc2luZ2xlIHBvaW50IGZvciBlYWNoIGNvdW50cnkgaW4gdGhlIGRhdGFzZXQgcmVwcmVzZW50aW5nIHRoZSBtZWFuIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+bGlmZUV4cDwvc3Bhbj4gYW5kIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+Z2RwUGVyY2FwPC9zcGFuPi4gTWFrZSBzdXJlIHRvIHJlYWQgdGhlIGNvZGUgdXNlZCB0byBnZW5lcmF0ZSB0aGUgY2hhcnQgY2FyZWZ1bGx5IHNvIHRoYXQgeW91IHVuZGVyc3RhbmQgaXQgZnVsbHkuIFdoeSBhcmUgdGhlIGNvbG91ciBhbmQgc2l6ZSBwYXJhbXRlcnMgaW4gdGhlIGBnZW9tX3BvaW50KClgIG5vdCBpbnNpZGUgYW4gYGFlcygpYD8KCjwvZGl2PgoKMi4gQm9udXMgcXVlc3Rpb246IENhbiB5b3UgbWFwIHRoZSA8c3BhbiBjbGFzcz0idmFyaWFibGUiPnBvcDwvc3Bhbj4gdG8gYSBwYXJ0aWN1bGFyIGFlc3RoZXRpYyBzbyB0aGF0IGl0IGNhbiBiZSBkaXNwbGF5ZWQgb24gdGhlIGNoYXJ0IHRvbz8KCjxkaXYgY2xhc3M9InNvbHV0aW9uIj4K8J+PhioqKlNvbHV0aW9uKioq8J+PhjogIExldHMgbWFwIHRoZSBzaXplIG9mIGVhY2ggcG9pbnQgdG8gdGhlIGF2ZXJhZ2UgcG9wdWxhdGlvbi4gSSBhbHNvIGludHJvZHVjZSBhbiBhZGRpdGlvbmFsIGFlc3RoZXRpYyBtYXBwaW5nIG9mIGNvbG91ciB0byB0aGUgY29udGluZW50IHZhcmlhYmxlLiBJbiBhZGRpdGlvbiwgc2luY2UgdGhlcmUgYXJlIGxhcmdlIHZhbHVlcyBpbiB0aGUgPHNwYW4gY2xhc3M9InZhcmlhYmxlIj5nZHBQZXJjYXA8L3NwYW4+LCB3ZSBjYW4gcGxvdCB0aGUgbG9nIG9mIHRoZSBtZWFuIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+Z2RwUGVyY2FwPC9zcGFuPiB0byBzbW9vdGggb3V0IHRoZSBsYXJnZSBudW1iZXJzICg8c3BhbiBjbGFzcz0iaGlnaGxpZ2h0Ij5bd2F0Y2hdKCNsb2dzKTwvc3Bhbj4gdGhlIHZpZGVvIGJlbG93IGlmIHlvdSBkb24ndCBmdWxseSB1bmRlcnN0YW5kIGxvZ2FyaXRobWljIHNjYWxlcykuIAoKV2UgY2FuIGRvIHRoaXMgaW4gdHdvIHdheXMsIGZpcnN0IHdlIGNvdWxkIG11dGF0ZSA8c3BhbiBjbGFzcz0idmFyaWFibGUiPm1lYW5HRFBwZXJDYXA8L3NwYW4+IGJ5IHRha2luZyBpdHMgbG9nIGFuZCBwbG90dGluZyB0aGF0ICh0cnkgdGhpcyB5b3Vyc2VsZilbXjRdLCBhbmQgdGhlIHNlY29uZCBvcHRpb24gd291bGQgYmUgdG8gdXNlIGdncGxvdHMgaW5idWlsdCBgc2NhbGVfeF9sb2cxMCgpYCBjb21tYW5kIHRvIHRyYW5zZm9ybSB0aGUgcGxvdHRpbmcgc2NhbGUgaW5zdGVhZCBvZiB0aGUgdmFyaWFibGUuIFdoaWxlIHRoZSBjaGFydHMgd291bGQgbGFyZ2VseSBsb29rIHRoZSBzYW1lLCB0aGUgc2Vjb25kIG9wdGlvbiBwcmVzZXJ2ZXMgdGhlIGFjdHVhbCB2YXJpYWJsZSBhbmQgcGxvdHMgaXQgb24gdGhlIG5ldyBzY2FsZS4gUGxvdCB0aGUgZmlyc3Qgb3B0aW9uIG9uIHlvdXIgb3duIHRvIHNlZSBpZiB5b3UgY2FuIHNwb3QgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcGxvdCBiZWxvdyBhbmQgdGhhdCBvbmUuCgpUaGUgYXBwbGljYXRpb24gb2YgdGhlIGxvZyBzY2FsZSBoYXMgdGhlIGVmZmVjdCBvZiBtYWtpbmcgaXQgYXBwZWFyIGFzIGlmIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiA8c3BhbiBjbGFzcz0idmFyaWFibGUiPmdkcFBlcmNhcDwvc3Bhbj4gYW5kIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+bGlmZUV4cDwvc3Bhbj4gaXMgbGluZWFyLiA8c3BhbiBjbGFzcz0iaGlnaGxpZ2h0Ij5UaGlzIGlzIG5vdCB0cnVlPC9zcGFuPiwgc2luY2UgYXMgd2Ugb2JzZXJ2ZWQgaW4gdGhlIHByZXZpb3VzIGNoYXJ0IHRoZSB0cnVlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZXNlIHR3byB2YXJpYWJsZXMgaXMgcGFyYWJvbGljIGkuZS4gYXMgPHNwYW4gY2xhc3M9InZhcmlhYmxlIj5nZHBQZXJjYXA8L3NwYW4+IGluY3JlYXNlcywgdGhlIGNvcnJlc3BvbmRpbmcgaW5jcmVhc2UgaW4gPHNwYW4gY2xhc3M9InZhcmlhYmxlIj5saWZlRXhwPC9zcGFuPiBnZXRzIGxvd2VyIG92ZXIgdGltZSAocHJvYmFibHkgYmVjYXVzZSB3ZSBjYW4ndCBleHRlbmQgaHVtYW4gbGlmZXNwYW5zIGJleW9uZCBhIGNlcnRhaW4gbGltaXQsIG5vIG1hdHRlciBob3cgaGFyZCB3ZSB0cnkpLiBUaGlzIGlzIG9uZSByZWFzb24sIHdoeSB3ZSBzaG91bGQgYmUgZXh0cmVtZWx5IGNhcmVmdWwgd2l0aCBzY2FsZSB0cmFuc2Zvcm1hdGlvbnMuIFdlIHNob3VsZCBvbmx5IHVzZSB0aGVtIGlmIHdlIGhhdmUgYSBjbGVhciByZWFzb24gZm9yIHdoeSB0aGV5IGFyZSBuZWNlc3NhcnkuIEFuZCB3aGVuIHdlIGRvIGNob29zZSB0byB1c2UgdGhlbSB3ZSBzaG91bGQgaGF2ZSBhIGNsZWFyIHNlbnNlIGZvciB3aGF0IHRoZXkgYXJlIGRvaW5nIHRvIHRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgY2hhcnQuCmBgYHtyfQpnYXBtaW5kZXIgJT4lIAogICAgIyNDYWxjdWxhdGUgY291bnRyeSBsZXZlbCBpbmZvcm1hdGlvbgogICAgZ3JvdXBfYnkoY291bnRyeSkgJT4lIAogICAgc3VtbWFyaXNlKG1lYW5HRFBwZXJDYXAgPSBtZWFuKGdkcFBlcmNhcCwgbmEucm0gPSBUKSwgCiAgICAgICAgICAgICAgbWVhbkxpZmVFeHAgPSBtZWFuKGxpZmVFeHAsIG5hLnJtID0gVCksIAogICAgICAgICAgICAgIG1lYW5Qb3AgPSBtZWFuKHBvcCwgbmEucm0gPSBUKSwKICAgICAgICAgICAgICBjb250aW5lbnQgPSB1bmlxdWUoY29udGluZW50KSkgJT4lIAogICAgIyNzcGVjaWZ5IGRhdGEgYW5kIGFlc3RoZXRpY3MKICAgIGdncGxvdCguLCBhZXMoeCA9IG1lYW5HRFBwZXJDYXAsIAogICAgICAgICAgICAgICAgICB5ID0gbWVhbkxpZmVFeHAsIAogICAgICAgICAgICAgICAgICBzaXplID0gbWVhblBvcCwgCiAgICAgICAgICAgICAgICAgIGNvbG91ciA9IGNvbnRpbmVudCkpICsgCiAgICAjI3NwZWNpZnkgdGhlIGdlb20KICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsKICAgICMjYXBwbHkgc2NhbGUgdHJhbnNmb3JtYXRpb24KICAgIHNjYWxlX3hfbG9nMTAoKSArCiAgICAjI3NwZWNpZnkgdGhlIHRpdGxlcwogICAgbGFicyh4ID0gIk1lYW4gR0RQIHBlciBjYXBpdGEiLCAKICAgICAgICAgeSA9ICJNZWFuIExpZmUgRXhwZWN0YW5jeSIsIAogICAgICAgICB0aXRsZSA9ICJMaWZlIEV4cGVjdGFuY3kgaW5jcmVhc2VzIGV4dGggR0RQIHBlciBjYXBpdGEiLCAKICAgICAgICAgc2l6ZSA9ICJNZWFuIHBvcHVsYXRpb24iLAogICAgICAgICBjb2xvdXIgPSAiQ29udGluZW50IikgKwogICAgIyNhZGp1c3QgdGhlIHBvc2l0aW9uIGFuZCBsYXlvdXQgb2YgdGhlIGxlZ2VuZHMKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgICAgbGVnZW5kLmJveCA9ICJ2ZXJ0aWNhbCIsCiAgICAgICAgICBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgtOCwgInB0IiksCiAgICAgICAgICApCmBgYAoKPC9kaXY+Cgo8ZGl2IGNsYXNzPSJjZW50ZXItY29udGFpbmVyIj4KPGEgbmFtZT0ibG9ncyI+PGlmcmFtZSB3aWR0aD0iNzIwIiBoZWlnaHQ9IjQwNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC9zQmhFaTRMOTFTZyIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhdXRvcGxheTsgZW5jcnlwdGVkLW1lZGlhIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+PC9hPgo8L2Rpdj4KCiMjIyBMaW5lIGNoYXJ0Ck5vdyBsZXRzIHVzZSBkcGx5ciB0byBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgZ2xvYmFsIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+bGlmZUV4cDwvc3Bhbj4gYnkgeWVhciBhbmQgcGxvdCB0aGF0IHVzaW5nIGEgbGluZS4gQXMgY2FuIGJlIHNlZW4gYmVsb3csIHRoZSBhdmVyYWdlIGdsb2JhbCA8c3BhbiBjbGFzcz0idmFyaWFibGUiPmxpZmVFeHA8L3NwYW4+IGhhcyBiZWVuIGluY3JlYXNpbmcgb3ZlciB0aGUgeWVhcnMuCgpgYGB7cn0KZ2FwbWluZGVyICU+JSAKICAgICMjZ3JvdXAgYnkgeWVhciB0byBjYWxjdWxhdGUgdGhlIHllYXJseSBhdmVyYWdlIGxpZmUgZXhwZWN0YW5jeSBmb3IgdGhlIHdvcmxkIChhbGwgY291bnRyaWVzKQogICAgZ3JvdXBfYnkoeWVhcikgJT4lIAogICAgc3VtbWFyaXNlKG1lYW5MaWZlRXhwID0gbWVhbihsaWZlRXhwLCBuYS5ybSA9IFQpKSAlPiUgCiAgICAjI2RyYXcgYSBsaW5lIG1hcmtpbmcgdGhlIHRyZW5kIG9mIGxpZmUgZXhwZWN0YW5jeSBvdmVyIHllYXJzCiAgICBnZ3Bsb3QoLiwgYWVzKHggPSB5ZWFyLCB5ID0gbWVhbkxpZmVFeHApKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgbGlmZSBleHBlY3RhbmN5IGhhcyBiZWVuIGluY3JlYXNpbmciLCB4ID0gIlllYXJzIiwgeSA9ICJNZWFuIExpZmUgRXhwZWN0YW5jeSIpCmBgYAoKTm93IGxldHMgZ28gYSBiaXQgZnVydGhlciBhbmQgZXhwbG9yZSB0aGUgdHJlbmQgZm9yIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+bGlmZUV4cDwvc3Bhbj4gZm9yIGRpZmZlcmVudCBjb3VudHJpZXMuIE5vCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlciwgbWFwcGluZyA9IGFlcyh4ID0geWVhciwgeSA9IGxpZmVFeHApKSArCiAgICAjI2NvbG91ciBhbmQgZ3JvdXAgYXJlIG1hcHBlZCB0byBjb250aW5lbnQgYW5kIGNvdW50cnkgYW5kIGFlc3RoZXRpYyBpcyBzZXQgdG8gMC41CiAgICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyhjb2xvdXIgPSBjb250aW5lbnQsIGdyb3VwID0gY291bnRyeSksIGFscGhhID0gMC41KSArCiAgICBsYWJzKHRpdGxlID0gIkNvdW50cmllcyBpbiBBZnJpY2EgaGF2ZSBsb3dlciBsaWZlIGV4cGVjdGFuY3kiLCB4ID0gIlllYXIiLCB5ID0gIkxpZmUgRXhwZWN0YW5jeSIpICsKICAgIHRoZW1lKAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiCiAgICAgICAgCiAgICApCmBgYAoKSW4gdGhlIGNoYXJ0IGFib3ZlIHRoZXJlIGFyZSBhIGZldyBjb3VudHJpZXMgdGhhdCBleHBlcmllbmNlIHN1ZGRlbiBkcm9wcyBpbiA8c3BhbiBjbGFzcz0idmFyaWFibGUiPmxpZmVFeHA8L3NwYW4+LiBUaGVzZSBhcmUgYmVjYXVzZSBvZiBnZW5vY2lkZXMgdGhhdCBvY2N1cnJlZCBpbiBSd2FuZGEsIENhbWJvZGlhIGFuZCBDaGluYS4gVGhlIGNoYXJ0IGJlbG93IHVzZWQgY29sb3IgYW5kIGFscGhhIG1hcHBpbmcgdG8gaGlnaGxpZ2h0IHRoZXNlIHZhbHVlcy4gTm90aWNlIGhvdywgSSB1c2UgYSBhbHBoYU1hcHBpbmcgYW5kIGNvbG9yTWFwcGluZyB2YXJpYWJsZXMgdG8gZml4IHRoZSBhbHBoYSBhbmQgY29sb3VyIGFlc3RoZXRpY3MgaW4gdGhlIGNoYXJ0LlteNl0KYGBge3J9CiMjcmVwZWF0IHRoZSBzYW1lIGJ1dCB3aXRoIG9uZSBsaW5lIGZvciBlYWNoIGNvbnRpbmVudC9jb3VudHJ5LCAoZ3Vlc3Mgd2h5IHRoZXJlIGlzIGEgc3VkZGVuIGRyb3AgZm9yIGEgZmV3IGNvdW50cmllcyk/CmFscGhhTWFwcGluZyA8LSBpZl9lbHNlKGdhcG1pbmRlciRjb3VudHJ5ICVpbiUgYygiUndhbmRhIiwgIkNhbWJvZGlhIiwgIkNoaW5hIiksIDAuOCwgMC4xKQpjb2xvck1hcHBpbmcgPC0gaWZfZWxzZShnYXBtaW5kZXIkY291bnRyeSAlaW4lIGMoIlJ3YW5kYSIsICJDYW1ib2RpYSIsICJDaGluYSIpLCAiZGFya3JlZCIsICJibGFjayIpCgpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlciwgbWFwcGluZyA9IGFlcyh4ID0geWVhciwgeSA9IGxpZmVFeHAsIGdyb3VwID0gY291bnRyeSkpICsKICAgIGdlb21fbGluZShhbHBoYSA9IGFscGhhTWFwcGluZywgY29sb3VyID0gY29sb3JNYXBwaW5nKSArCiAgICBsYWJzKHRpdGxlID0gIlRoZSBpbXBhY3Qgb2YgZ2Vub2NpZGVzIG9uIGxpZmUgZXhwZWN0YW5jeSIsIHggPSAiWWVhciIsIHkgPSAiTGlmZSBFeHBlY3RhbmN5IikKYGBgCgoK4pqhKioqTmluamEgVGFza3MqKirimqEKCjEuIERyYXcgYSBsaW5lIGNoYXJ0IHNob3dpbmcgdGhlIHRyZW5kIGZvciBhdmVyYWdlIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+Z2RwUGVyY2FwPC9zcGFuPiB0aHJvdWdoIHRpbWUgZm9yIGVhY2ggY29udGluZW50Cgo8ZGl2IGNsYXNzPSJzb2x1dGlvbiI+CvCfj4YqKipTb2x1dGlvbioqKvCfj4Y6IFRoZSBjaGFydCBiZWxvdyBzaG93cyB0aGF0IHBvb3JlciBjb3VudHJpZXMgaW4gdGhlIEFmcmljYSBhbmQgQXNpYSBhcmUgc3RpbGwgbGFnZ2luZyBiZWhpbmQgdGhvc2UgaW4gdGhlIFdlc3QuIEFjY29yZGluZyB0byBbV2lraXBlZGlhXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db252ZXJnZW5jZV8oZWNvbm9taWNzKSkgY29udmVyZ2VuY2UgY2FuIGJlIGRlc2NyaWJlZCBhcyBmb2xsb3dzOgoKPlRoZSBpZGVhIG9mIGNvbnZlcmdlbmNlIGluIGVjb25vbWljcyAoYWxzbyBzb21ldGltZXMga25vd24gYXMgdGhlIGNhdGNoLXVwIGVmZmVjdCkgaXMgdGhlIGh5cG90aGVzaXMgdGhhdCBwb29yZXIgZWNvbm9taWVzJyBwZXIgY2FwaXRhIGluY29tZXMgd2lsbCB0ZW5kIHRvIGdyb3cgYXQgZmFzdGVyIHJhdGVzIHRoYW4gcmljaGVyIGVjb25vbWllcy4gQXMgYSByZXN1bHQsIGFsbCBlY29ub21pZXMgc2hvdWxkIGV2ZW50dWFsbHkgY29udmVyZ2UgaW4gdGVybXMgb2YgcGVyIGNhcGl0YSBpbmNvbWUuCgpgYGB7cn0KZ2FwbWluZGVyICU+JSAKICAgIGdyb3VwX2J5KGNvbnRpbmVudCwgeWVhcikgJT4lIAogICAgc3VtbWFyaXNlKG1lYW5HRFAgPSBtZWFuKGdkcFBlcmNhcCwgbmEucm0gPSBUKSkgJT4lIAogICAgZ2dwbG90KC4sIGFlcyh4ID0geWVhciwgeSA9IG1lYW5HRFApKSArCiAgICBnZW9tX2xpbmUoYWVzKGNvbG91ciA9IGNvbnRpbmVudCkpICsKICAgIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJNZWFuIEdEUCBwZXIgQ2FwaXRhIiwgdGl0bGUgPSAiQ29udmVyZ2VuY2Ugd2hlcmUgYXJ0IHRob3U/IiwgY29sb3VyID0gIkNvbnRpbmVudCIpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCjwvZGl2PgoKMy4gSGlzdG9ncmFtCldhdGNoIHRoZSB2aWRlbyBbYmVsb3ddKCNoaXN0KSB0byByZWZyZXNoIHlvdXIgdW5kZXJzdGFuZGluZyBvZiBoaXN0b2dyYW1zLiBUaGUgcGxvdCBiZWxvdyBzaG93cyB0aGUgaGlzdG9ncmFtIG9mIGBnZHBQZXJDYXBgIGluIHRoZSBnYXBtaW5kZXIgZGF0YXNldC4gVGhlcmUgaXMgaG93ZXZlciBhIHByb2JsZW0gd2l0aCB0aGlzIGNoYXJ0LiBJdCBzaG93cyB0aGUgZGlzdHJpYnV0aW9uIG9mIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+Z2RwUGVyY2FwPC9zcGFuPiBvdmVyIHRpbWUuIFdlIGFyZSBob3dldmVyIG5vdCBpbnRlcmVzdGVkIGluIHRoZSBzcHJlYWQgb2YgPHNwYW4gY2xhc3M9InZhcmlhYmxlIj5nZHBQZXJjYXA8L3NwYW4+IGFjcm9zcyB0aW1lLCByYXRoZXIgb3VyIGludGVyZXN0IGlzIGluIHNlZWluZyB0aGUgc3ByZWFkIGFjcm9zcyBjb3VudHJpZXMuIEluIHRoaXMgY2FzZSwgd2UgbWlnaHQgYmUgYmV0dGVyIG9mZiBieSBmaWx0ZXJpbmcgZG93biB0byBhIHNpbmdsZSB5ZWFyIGFuZCBvYnNlcnZpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiA8c3BhbiBjbGFzcz0idmFyaWFibGUiPmdkcFBlcmNhcDwvc3Bhbj4gYWNyb3NzIGNvdW50cmllcy4gTGV0cyB0cnkgdGhpcyBvdXQgd2l0aCB0aGUgdHJhaW5pbmcgZXhlcmNpc2UuCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlciwgYWVzKGdkcFBlcmNhcCkpICsKICAgIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCkKYGBgCjxkaXYgY2xhc3M9ImNlbnRlci1jb250YWluZXIiPgo8YSBpZD0iaGlzdCI+PGlmcmFtZSB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC9nU0VZdEFqdVotWSIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhdXRvcGxheTsgZW5jcnlwdGVkLW1lZGlhIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+PC9hPgo8L2Rpdj4KCuKaoSoqKk5pbmphIFRhc2tzKioq4pqhCgoxLiBEcmF3IGEgaGlzdG9ncmFtIHRvIHNob3cgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiBwb3B1bGF0aW9uCgo8ZGl2IGNsYXNzPSJzb2x1dGlvbiI+CvCfj4YqKipTb2x1dGlvbioqKvCfj4Y6IEZvciB0aGlzIGNoYXJ0IHdlIHNlbGVjdCB0aGUgbGF0ZXN0IHllYXIgaW4gdGhlIGRhdGFzZXQgYW5kIHBsb3QgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcG9wdWxhdGlvbi4gQ291bGQgeW91IHRoaW5rIG9mIHRoZSBwcm9zIGFuZCBjb25zIGZvciB1c2luZyB0aGlzIG1ldGhvZCB2ZXJzdXMgdGhlIG9uZSBpbiB3aGljaCB3ZSBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgcG9wdWxhdGlvbiBmb3IgZWFjaCBjb3VudHJ5IG92ZXIgdGhlIGVudGlyZSBkYXRhc2V0IGFuZCBwbG90dGluZyB0aGF0PwoKYGBge3J9CmdhcG1pbmRlciAlPiUgCiAgICBmaWx0ZXIoeWVhciA9PSBtYXgoeWVhciwgbmEucm0gPSBUKSkgJT4lIAogICAgZ2dwbG90KC4sIGFlcyhwb3ApKSArCiAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNjApICsKICAgIGxhYnMoeCA9ICJQb3B1bGF0aW9uIiwgeSA9ICJDb3VudCIpCmBgYAoKPC9kaXY+CgojIyNCYXIgY2hhcnQKVGhlIGNoYXJ0IGJlbG93IHNob3dzIHRoZSBhdmVyYWdlIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+bGlmZUV4cDwvc3Bhbj4gZm9yIGRpZmZlcmVudCBjb250aW5lbnRzIGZvciB0aGUgbW9zdCByZWNlbnQgeWVhciBpbiB0aGUgZGF0YS4gQ2FuIHlvdSB0aGluayBvZiB0aGUgcmVhc29uIHdoeSBpdCBtaWdodCBiZSBzbGlnaHRseSBiZXR0ZXIgdG8gaGF2ZSBvbmx5IGNvbnNpZGVyZWQgdGhlIG1vc3QgcmVjZW50IHllYXIgd2hlbiBjYWxjdWxhdGluZyB0aGUgYXZlcmFnZSA8c3BhbiBjbGFzcz0idmFyaWFibGUiPmxpZmVFeHA8L3NwYW4+IGZvciBhIGNvbnRpbmVudD8gQWxzbyBub3RpY2UsIGhvdyB0aGUgYmFycyBhcmUgYWxpZ25lZCBmcm9tIHRoZSBzbWFsbGVzdCB0byB0aGUgdGFsbGVzdC4gQ2FuIHlvdSBmaW5kIG91dCBob3cgSSBtaWdodCBoYXZlIGFjaGlldmVkIHRoaXM/CmBgYHtyfQojI2RyYXcgYSBiYXIgY2hhcnQgd2l0aCBhdmVyYWdlIGxpZmUgZXhwZWN0YW5jeSBpbiBkaWZmZXJlbnQgY29udGluZW50cwpnYXBtaW5kZXIgJT4lIAogICAgZmlsdGVyKHllYXIgPT0gbWF4KHllYXIsIG5hLnJtID0gVCkpICU+JSAKICAgIGdyb3VwX2J5KGNvbnRpbmVudCkgJT4lIAogICAgc3VtbWFyaXNlKG1lYW5MaWZlRXhwID0gbWVhbihsaWZlRXhwLCBuYS5ybSA9IFQpKSAlPiUgCiAgICBnZ3Bsb3QoLiwgYWVzKHggPSByZW9yZGVyKGNvbnRpbmVudCwgbWVhbkxpZmVFeHApLCB5ID0gbWVhbkxpZmVFeHApKSArCiAgICBnZW9tX2NvbCgpICsKICAgIGxhYnMoeSA9ICJNZWFuIGxpZmUgZXhwZWN0YW5jeSIpICsKICAgIHRoZW1lKAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKQogICAgKQogICAgCmBgYAoK4pqhKioqTmluamEgVGFza3MqKirimqEKCjEuIERyYXcgYSBiYXIgY2hhcnQgc2hvd2luZyB0aGUgYXZlcmFnZSBsaWZlRXhwIGFjcm9zcyB5ZWFycwoKPGRpdiBjbGFzcz0ic29sdXRpb24iPgrwn4+GKioqU29sdXRpb24qKirwn4+GCmBgYHtyfQpnYXBtaW5kZXIgJT4lIAogICAgZ3JvdXBfYnkoeWVhcikgJT4lIAogICAgc3VtbWFyaXNlKG1lYW5MaWZlRXhwID0gbWVhbihsaWZlRXhwLCBuYS5ybSA9IFQpKSAlPiUgCiAgICBnZ3Bsb3QoZGF0YSA9IC4sIGFlcyh4ID0geWVhciwgeSA9IG1lYW5MaWZlRXhwKSkgKwogICAgZ2VvbV9jb2woKQogICAgCmBgYAoKCmBgYHtyfQpnYXBtaW5kZXIgJT4lIAogICAgZ3JvdXBfYnkoeWVhcikgJT4lIAogICAgc3VtbWFyaXNlKG1lYW5HRFAgPSBtZWFuKGdkcFBlcmNhcCwgbmEucm0gPSBUKSkgJT4lIAogICAgZ2dwbG90KC4sIGFlcyh4ID0geWVhciwgeSA9IG1lYW5HRFApKSArCiAgICBnZW9tX2NvbCgpCmBgYAo8L2Rpdj4KCiMjI0ZhY2V0dGluZwoKYGBge3J9CiMjcmV1c2Ugc29tZSBvZiB0aGUgcGxvdHMgYWJvdmUgYXMgYSBmYWNldHRlZCBwbG90CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0geWVhciwgeSA9IGxpZmVFeHApKSArCiAgICBnZW9tX2ppdHRlcih3aWR0aCA9IDEuNSwgYWxwaGEgPSAwLjYpICsKICAgIGZhY2V0X3dyYXAofmNvbnRpbmVudCkKYGBgCgoK4pqhKioqTmluamEgVGFza3MqKirimqEKCjEuIFNob3cgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+Z2RwUGVyY2FwPC9zcGFuPiBhbmQgPHNwYW4gY2xhc3M9InZhcmlhYmxlIj5saWZlRXhwPC9zcGFuPiB1c2luZyBhIGZhY2V0ZWQgY2hhcnQgb2YgeW91ciBjaG9pY2UKCgoKCiMjI0xheWVyaW5nIG11bHRpcGxlIGdlb21zCgoKYGBge3J9CiMjZHJhdyBhIGxpbmUgb2YgZml0IGluIGEgY2hhcnQgb2YgcG9wdWxhdGlvbiBhbmQgbGlmZSBleHBlY3RhbmN5CmdhcG1pbmRlciAlPiUgCiAgICBncm91cF9ieShjb3VudHJ5KSAlPiUgCiAgICBzdW1tYXJpc2UoY29udGluZW50ID0gdW5pcXVlKGNvbnRpbmVudCksIG1lYW5Qb3AgPSBtZWFuKHBvcCwgbmEucm0gPSBUKSwgbWVhbkxpZmVFeHAgPSBtZWFuKGxpZmVFeHAsIG5hLnJtID0gVCkpICU+JSAKICAgIGZpbHRlcihtZWFuUG9wIDwgNWU3KSAlPiUgCmdncGxvdCguLCBhZXMoeCA9IG1lYW5Qb3AsIHkgPSBtZWFuTGlmZUV4cCkpICsKICAgIGdlb21faml0dGVyKCkgKyAKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRikKYGBgCgoK4pqhKioqTmluamEgVGFza3MqKirimqEKCjEuIEFkZCBhIGxpbmUgb2YgYmVzdCBmaXQgdG8gYSBzY2F0dGVyIHBsb3Qgc2hvd2luZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gPHNwYW4gY2xhc3M9InZhcmlhYmxlIj5nZHBQZXJjYXA8L3NwYW4+IGFuZCA8c3BhbiBjbGFzcz0idmFyaWFibGUiPmxpZmVFeHA8L3NwYW4+CgojIyNBZGRpbmcgc3VtbWFyeSBzdGF0cyB0byBwbG90cwoKCmBgYHtyfQojI2V4cGxvcmUgZ2RwIHBlciBjYXBpdGEgb3ZlciB0aGUgeWVhcnMgdXNpbmcgYSBqaXR0ZXIgcGxvdCBhbmQgc3RhdF9zdW1tYXJ5IChydWxlIG9mIHRodW1iLCBhbHdheXMgc3RheSBhcyBjbG9zZSB0byB0aGUgZGF0YSBhcyBwb3NzaWJsZSkKCiAgICAKYGBgCgoKCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSB5ZWFyLCB5ID0gZ2RwUGVyY2FwKSkgKwogICAgZ2VvbV9qaXR0ZXIoKQpgYGAKCmBgYHtyfQojI1VzaW5nIHZsaW5lIGFuZCBobGluZQoKYGBgCgoKCuKaoSoqKk5pbmphIFRhc2tzKioq4pqhCgoxLiBBZGQgYSB2ZXJ0aWNhbCBsaW5lIHNob3dpbmcgdGhlIG1lYW4gb2YgcG9wdWxhdGlvbiB0byB0aGUgaGlzdG9ncmFtIHNob3dpbmcgaXRzIGRpc3RyaWJ1dGlvbgoKCiMjI0FkZCBzb21lIHN0eWxlCgoKYGBge3J9CiMjVXNlIHRoZSBwbG90IGZyb20gdGhlIHByZXZpb3VzIHNlY3Rpb24gYW5kIGFkZCBzb21lIHBpemF6eiAoZXhwbG9yZSBnZ3RoZW1lcywgbGVnZW5kIHBvc2l0aW9uIGV0YykKYGBgCgoKCgojIyNBZGQgc29tZSBtb3JlIGNvbXBsZXhpdHkgKGJhc2VkIG9uIHRpbWUpCgojIyMjQ29vcmRzCgojIyMjU2NhbGVzCgoKIyNEYXRhIEpvaW5zCkEgZGF0YSBqb2luIGNvbWJpbmVzIHR3byBvciBtb3JlIGRpZmZlcmVudCBkYXRhIHRhYmxlcyBpbnRvIG9uZS4gVGFibGVzIHRoYXQgY2FuIGJlIGNvbm5lY3RlZCB0byBlYWNoIG90aGVyIGNhbiBiZSB0aG91Z2h0IG9mIGFzIGJlaW5nICJyZWxhdGVkIi4gQSBncm91cCBvZiB0d28gb3IgbW9yZSByZWxhdGVkIGRhdGEgdGFibGVzIHdvdWxkIGNvbnN0aXR1dGUgYSA8c3BhbiBjbGFzcz0iaGlnaGxpZ2h0Ij5yZWxhdGlvbmFsIGRhdGFiYXNlPC9zcGFuPi4gVGhlIHZhcmlhYmxlcyB0aGF0IGNvbm5lY3QgdHdvIG9yIG1vcmUgdGFibGVzIHdpdGggZWFjaCBvdGhlciBhcmUgY2FsbGVkIDxzcGFuIGNsYXNzPSJoaWdobGlnaHQiPmtleXM8L3NwYW4+LiBGb3IgaW5zdGFuY2UsIGNvbnNpZGVyIHR3byB0YWJsZXMsIG9uZSB3aXRoIGluZm9ybWF0aW9uIG9uIEdEUCBvZiBjb3VudHJpZXMgYW5kIGFub3RoZXIgd2l0aCB0aGUgcG9wdWxhdGlvbiBvZiBjb3VudHJpZXMuIFRoZXNlIHR3byB0YWJsZXMgYXJlIHJlbGF0ZWQgdG8gb25lIGFub3RoZXIgdGhyb3VnaCB0aGUgc2hhcmVkIGtleSBvZiBjb3VudHJ5LiBJZiB3ZSB3ZXJlIHRvIG1hbnVhbGx5IGNhbGN1bGF0ZSA8c3BhbiBjbGFzcz0idmFyaWFibGUiPmdkcFBlcmNhcDwvc3Bhbj4gb2YgYSBzcGVjaWZpYyBjb3VudHJ5IHdlIHdvdWxkIGxvb2sgZm9yIHRoZSBjb3JyZXNwb25kaW5nIHZhbHVlcyBmb3IgdGhlIGNvdW50cnkgaW4gZWFjaCB0YWJsZSB0byBleHRyYWN0IHRoZSByZXNwZWN0aXZlIHZhbHVlcyBvZiBHRFAgYW5kIHBvcHVsYXRpb24gdG8gY2FsY3VsYXRlIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+Z2RwUGVyY2FwPC9zcGFuPiAoR0RQL3BvcHVsYXRpb24pLgoKV2Ugd2lsbCBiZSB1c2luZyB0aGUgcHJhY3RpY2UgZGF0YXNldHMgYGJhbmRfbWVtYmVyc2AgYW5kIGBiYW5kX2luc3RydW1lbnRzYCBwcm92aWRlZCBpbiB0aGUgYGRwbHlyYCBwYWNrYWdlIHRvIHVuZGVyc3RhbmQgdGhlIHZhcmlvdXMgY29uY2VwdHMgaW4gZGF0YSBqb2lucy4gSSBoYXZlIGFkZGVkIGFuIGFkZGl0aW9uYWwgcm93IHRvIHRoZSBgYmFuZF9pbnN0cnVtZW50c2AgdGFibGUgdG8gbWFrZSBpdCBlYXNpZXIgdG8gaWxsdXN0cmF0ZSBhIGZldyBjb25jZXB0cy4gUGxlYXNlIHRha2UgYSBtb21lbnQgdG8gbG9vayBhdCB0aGUgdHdvIGRhdGEgdGFibGVzLgoKYGBge3J9CiMjZGlzcGxheSBiYW5kX21lbWJlcnMKYmFuZF9tZW1iZXJzCmBgYAoKYGBge3J9CiMjcmVtb3ZlIGJhbmRfbWVtYmVycyBmcm9tIHRoZSBsb2NhbCBlbnZpcm9ubWVudApybShiYW5kX2luc3RydW1lbnRzKQoKIyNhZGQgYSByb3cgdG8gYmFuZF9pbnN0cnVtZW50cyAoZ2l2ZSBKb2huIGFuIGV4dHJhIGluc3RydW1lbnQpCmJhbmRfaW5zdHJ1bWVudHMgPC0gYmluZF9yb3dzKGJhbmRfaW5zdHJ1bWVudHMsIHRpYmJsZShuYW1lID0gIkpvaG4iLCBwbGF5cyA9ICJrZXlib2FyZHMiKSkKCiMjZGlzcGxheSBiYW5kX2luc3RydW1lbnRzCmJhbmRfaW5zdHJ1bWVudHMKYGBgCgpLZXlzIGNhbiBiZSBvZiB0d28gdHlwZXMgLSBwcmltYXJ5IGFuZCBmb3JlaWduLiBBIGtleSB0aGF0IHVuaXF1ZWx5IGlkZW50aWZpZXMgZWFjaCByb3cgaW4gYSBwYXJ0aWN1bGFyIGRhdGFzZXQgaXMgaXRzIHByaW1hcnkga2V5LiBGb3IgaW5zdGFuY2UgaW4gdGhlIHRpYmJsZSBgYmFuZF9tZW1iZXJzYCB0aGUgcHJpbWFyeSBrZXkgaXMgdGhlIHZhcmlhYmxlIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+bmFtZTwvc3Bhbj4gc2luY2UgZWFjaCByb3cgY2FuIGJlIHVuaXF1ZWx5IGlkZW50aWZpZWQgaWYgd2Uga25vdyB0aGUgdmFsdWUgb2YgPHNwYW4gY2xhc3M9InZhcmlhYmxlIj5uYW1lPC9zcGFuPi4gV2UgY2FuIGNoZWNrIGlmIGEgdmFyaWFibGUgaXMgYSBwcmltYXJ5IGtleSBieSBncm91cGluZyB0aGUgZGF0YSBhbmQgY2hlY2tpbmcgdGhlIG51bWJlciBvZiByb3dzIGluIGVhY2ggZ3JvdXAuIElmIHRoZXJlIGFyZSBtb3JlIHRoYW4gb25lIHJvdyBpbiBhIGdyb3VwIGl0IG1lYW5zIHRoYXQgdGhlIHBhcnRpY3VsYXJseSBncm91cGluZyB2YXJpYWJsZSBkb2VzIG5vdCB1bmlxdWVseSBpZGVudGlmeSBlYWNoIHJvdyBpbiB0aGUgZGF0YSBpLmUuIHRoZXJlIGFyZSBtdWx0aXBsZSByb3dzIHRoYXQgaGF2ZSB0aGUgc2FtZSB2YWx1ZSBmb3IgdGhhdCBncm91cGluZyB2YXJpYWJsZSwgaGVuY2UgaXQgaXMgbm90IGEgcHJpbWFyeSBrZXkuIApgYGB7cn0KIyNwcm9vZiB0aGF0IG5hbWUgaXMgYSBwcmltYXJ5IGtleSBmb3IgYmFuZF9tZW1iZXJzCmJhbmRfbWVtYmVycyAlPiUgCiAgICBncm91cF9ieShuYW1lKSAlPiUgCiAgICBmaWx0ZXIobigpID4gMSkKYGBgCgpBIHByaW1hcnkga2V5IGNhbiBhbHNvIGJlIGEgY29tYmluYXRpb24gb2YgdHdvIG9yIG1vcmUgdmFyaWFibGVzLiBGb3IgaW5zdGFuY2UsIGluIHRoZSBjYXNlIG9mIGJhbmRfaW5zdHJ1bWVudHMsIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+bmFtZTwvc3Bhbj4gYWxvbmUgZG9lcyBub3QgdW5pcXVlbHkgaWRlbnRpZnkgZWFjaCByb3cgaW4gdGhlIGRhdGEgKHNpbmNlIHdlIGFkZGVkIGFuIGFkZGl0aW9uYWwgcm93IGZvciBKb2huKS4gSG93ZXZlciwgd2UgY2FuIGNvbWJpbmUgdGhlIHZhcmlhYmxlcyBuYW1lIGFuZCBwbGF5IHRvIHVuaXF1ZWx5IGlkZW50aWZ5IGV2ZXJ5IHJvdyBpbiB0aGUgZGF0YQoKVGhlcmUgYXJlIDYgZGlmZmVyZW50IHR5cGVzIG9mIGpvaW5zLiBGb3VyIG9mIHRoZXNlIGFyZSBjYWxsZWQgPHNwYW4gY2xhc3M9ImhpZ2hsaWdodCI+bXV0YXRpbmcgam9pbnM8L3NwYW4+IGFuZCB0d28gYXJlIGNhbGxlZCA8c3BhbiBjbGFzcz0iaGlnaGxpZ2h0Ij5maWx0ZXJpbmc8L3NwYW4+LiBBIG11dGF0aW5nIGpvaW4gYmV0d2VlbiB0d28gdGFibGVzICJ4IiBhbmQgInkiIGNyZWF0ZXMgYSBuZXcgdGFibGUgd2l0aCBjb2x1bW5zIGZyb20gYm90aCB4IGFuZCB5LiBBIGZpbHRlcmluZyBqb2luIGJldHdlZW4geCBhbmQgeSBjcmVhdGVzIGEgbmV3IHRhYmxlIHdpdGggYSBzdWJzZXQgb2YgdGhlIHJvd3MgZnJvbSB4IGFuZCB5LiBBIG11dGF0aW5nIGpvaW4gY2FuIGFsc28gbGVhZCB0byBhIHN1YnNldHRpbmcgb2Ygcm93cy4gTm93IGxldHMgbG9vayBhdCBlYWNoIGpvaW4gaW4gZGV0YWlsIAoKCgoKVGhlIG91dHB1dCBiZWxvdyBzaG93cyBhbiBpbm5lciBqb2luLiBIZXJlIHdlIGhhdmUgdXNlZCB0aGUgY29tbW9uIGtleSB2YXJpYWJsZSAibmFtZSIgdG8gcGVyZm9ybSBhbiBpbm5lciBqb2luIG9mIHRoZSBiYW5kX21lbWJlcnMgYW5kIGJhbmRfaW5zdHJ1bWVudHMuIFRoZSBpbm5lciBqb2luICBhbmQgYWxsIHRoZSBjb2x1bW5zIGZyb20geCBhbmQgeW9ubHkga2VlcHMgdGhlIHJvd3MgdGhhdCBoYXZlIGNvbW1vbiBrZXlzPC9zcGFuPiBiZXR3ZWVuIHggYW5kIHkuIFRoZSBncmFwaGljIGJlbG93IGlsbHVzdHJhdGVzIGFuIGBpbm5lcl9qb2luKClgCmBgYHtyfQppbm5lcl9qb2luKHggPSBiYW5kX21lbWJlcnMsIHkgPSBiYW5kX2luc3RydW1lbnRzLCBieSA9ICJuYW1lIikKYGBgCgo8ZGl2IGNsYXNzPSJjZW50ZXItY29udGFpbmVyIj4KYGBge3IsIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJhc3NldHMvaW1hZ2VzL2pvaW4taW5uZXIucG5nIikKYGBgCjwvZGl2Pgo8c21hbGw+KipTb3VyY2UqKjogUiBmb3IgRGF0YSBTY2llbmNlIGJ5IEdhcnJldHQgR3JvbGVtdW5kIGFuZCBIYWRsZXkgV2lja2hhbTwvc21hbGw+CgpgbGVmdF9qb2luKClgIGlzIHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgam9pbi4gSXQgcHJlc2VydmVzIGFsbCB0aGUgcm93cyBpbiB4IGFuZCAKYGBge3J9CmxlZnRfam9pbih4ID0gYmFuZF9tZW1iZXJzLCB5ID0gYmFuZF9pbnN0cnVtZW50cywgYnkgPSAibmFtZSIpCmBgYAoKCmBgYHtyfQpyaWdodF9qb2luKGJhbmRfbWVtYmVycywgYmFuZF9pbnN0cnVtZW50cykKYGBgCgpgYGB7cn0KZnVsbF9qb2luKGJhbmRfbWVtYmVycywgYmFuZF9pbnN0cnVtZW50cykKYGBgCgoKCmBgYHtyfQpzZW1pX2pvaW4oYmFuZF9tZW1iZXJzLCBiYW5kX2luc3RydW1lbnRzKQpgYGAKCgpgYGB7cn0KYW50aV9qb2luKGJhbmRfbWVtYmVycywgYmFuZF9pbnN0cnVtZW50cykKYGBgCgoKIyMjTGVmdCBKb2luClRoaXMgaXMgdGhlIG1vc3QgcG9wdWxhciB0eXBlIG9mIGpvaW4gKGFuYWxvZ291cyB0byBWbG9va3VwIGluIEV4Y2VsKS4KYGBge3J9CmxpYnJhcnkobnljZmxpZ2h0czEzKQoKZmxpZ2h0c1dpdGhXZWF0aGVyIDwtIGxlZnRfam9pbihmbGlnaHRzLCB3ZWF0aGVyLCBieSA9IGMoIm9yaWdpbiIsICJtb250aCIsICJkYXkiLCAiaG91ciIpKQpgYGAKCgpgYGB7cn0Kd2VhdGhlcgpgYGAKCgoKClteMV06IFRoZSBnYXBtaW5kZXIgcGFja2FnZSBpcyBhbiBleGNlcnB0IG9mIHRoZSBkYXRhIHRoYXQgZXhpc3RzIFtoZXJlXShodHRwczovL3d3dy5nYXBtaW5kZXIub3JnL2RhdGEvKS4gVGhpcyBkYXRhIHdhcyBjcmVhdGVkIGJ5IHRoZSBHYXBtaW5kZXIgRm91bmRhdGlvbiBsZWQgYnkgSGFucyBSb3NsaW5nLgpbXjJdOiBPdmVyLXBsb3R0aW5nIGhhcHBlbnMgd2hlbiBkYXRhIHBvaW50cyBhcmUgcGxvdHRlZCBvbiB0b3Agb2YgZWFjaCBvdGhlciBtdWx0aXBsZSB0aW1lcy4gSXQgbWFrZXMgY2hhcnRzIG1vcmUgY29uZnVzaW5nIGFuZCBkaWZmaWN1bHQgdG8gcmVhZC4gSXQgc2hvdWxkIGJlIGF2b2lkZWQgYXMgZmFyIGFzIHBvc3NpYmxlClteM106IFdlIGNvdWxkIGFsc28gZG8gdGhpcyBieSBmaWx0ZXJpbmcgb3V0IHRoZSBtb3N0IHJlY2VudCB5ZWFyIHRvIHN0dWR5IHRoZSByZWxhdGlvbnNoaXAuIEJvdGggYXJlIHZhbGlkIG9wdGlvbnMsIGhvd2V2ZXIgdGhlIGZvcm1lciBoYXMgdGhlIGJlbmVmaXQgb2YgY2FwdHVyaW5nIG1vcmUgaW5mb3JtYXRpb24gc2luY2UgaXQgdGFrZXMgaW50byBhY2NvdW50IHRoZSBlbnRpcmUgdGltZSBzZXJpZXMgb2YgZGF0YSB0aGF0IGlzIGF2YWlsYWJsZSB0byBjYWxjdWxhdGUgdGhlIG1lYW4gdmFsdWVzLCB3aGlsZSB0aGUgbGF0dGVyIGRpc3JlZ2FyZHMgYWxsIGJ1dCBvbmUgeWVhciBvZiB0aGUgZGF0YS4gSW4gdGhpcyBjYXNlIHRoaXMgc3VpdHMgb3VyIHB1cnBvc2VzIG9mIHdhbnRpbmcgdG8gc3R1ZHkgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIDxzcGFuIGNsYXNzPSJ2YXJpYWJsZSI+Z2RwUGVyY2FwPC9zcGFuPiBhbmQgPHNwYW4gY2xhc3M9InZhcmlhYmxlIj5saWZlRXhwPC9zcGFuPi4KW140XTogWW91IGNhbiBkbyB0aGlzIHVzaW5nIHRoZSBmb2xsb3dpbmcgY29tbWFuZCBgZ2dwbG90KC4sIGFlcyh4ID0gbG9nKG1lYW5HRFBwZXJDYXAsIGJhc2UgPSAxMCksIHkgPSBtZWFuTGlmZUV4cCkpYCBpbnN0ZWFkIG9mIGBnZ3Bsb3QoLiwgYWVzKHggPSBtZWFuR0RQcGVyQ2FwLCB5ID0gbWVhbkxpZmVFeHApKWAuIENhbiB5b3UgdGVsbCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoaXMgcGxvdCBhbmQgdGhlIG9uZSB0aGF0IHdhcyBtYWRlIHVzaW5nIHRoZSBzY2FsZSB0cmFuc2Zvcm1hdGlvbj8KW141XTogVGhpczUgYWVzdGhldGljIHBhcmFtZXRlcnMgYXJlIG5vdCBiZWluZyBtYXBwZWQgdG8gdGhlIGRhdGEuIEluc3RlYWQgd2UgYXJlIHVzaW5nIHZlY3RvcnMgdGhhdCB3ZXJlIGNyZWF0ZWQgb3V0c2lkZSB0aGUgY3VycmVudCBmdW5jdGlvbiBjYWxsIHRvIHNwZWNpZnkgdGhlIHZhbHVlcyBmb3IgdGhlc2UgdmVjdG9ycy4K